/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef MCPY_BUILTINS_CAST_H
#define MCPY_BUILTINS_CAST_H

#include <mc/array.h>
#include <mc/dict.h>
#include <mc/variant.h>
#include <optional>
#include <type_traits>

namespace mcpy {

template <typename T>
struct is_mc_array : std::false_type {
};

template <typename T, typename Allocator>
struct is_mc_array<mc::array<T, Allocator>> : std::true_type {
};

template <typename T>
mc::array<T> array_cast(const mc::variants& vars);

template <typename T, typename U>
T cast(U&& value) {
    using DecayedU = std::decay_t<U>;
    using DecayedT = std::decay_t<T>;

    if constexpr (std::is_same_v<DecayedU, DecayedT>) {
        return std::forward<U>(value);
    } else if constexpr (std::is_same_v<DecayedU, mc::variant>) {
        return value.template as<T>();
    } else if constexpr (std::is_same_v<DecayedU, mc::variant_reference>) {
        return value.template as<T>();
    } else if constexpr (std::is_convertible_v<DecayedU, DecayedT>) {
        return static_cast<T>(std::forward<U>(value));
    } else if constexpr (std::is_same_v<DecayedU, mc::variants> && is_mc_array<DecayedT>::value) {
        return array_cast<typename DecayedT::value_type>(value);
    } else {
        static_assert(std::is_convertible_v<DecayedU, DecayedT>,
                      "Cannot convert source type to target type");
        return static_cast<T>(value);
    }
}

template <typename T>
mc::array<T> array_cast(const mc::variants& vars) {
    mc::array<T> result;
    result.reserve(vars.size());
    for (const auto& var : vars) {
        result.push_back(cast<T>(var));
    }
    return result;
}

} // namespace mcpy

#endif // MCPY_BUILTINS_CAST_H
